﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RemotingLibs.QuoteClass;
using TdxApiPool.Connect;
using TdxApiPool.ErrorHandler;

namespace TdxApiPool.Invoker
{
    public class Quote
    {
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Quote");

        /// <summary>
        /// 获取历史分数据
        /// </summary>
        /// <param name="tdxConnection"></param>
        /// <param name="stklist"></param>
        /// <returns></returns>
        public static List<StkMin> GetHistoryMinuteTimeData(TdxConnection tdxConnection, List<Stkinfo> stkList)
        {
            StringBuilder sErrInfo = new StringBuilder(256);
            StringBuilder sResult = new StringBuilder(1024 * 1024);


            QuoteDelegate.TdxHq_GetHistoryMinuteTimeData tdxHqGetHistoryTransactionData =
                (QuoteDelegate.TdxHq_GetHistoryMinuteTimeData) tdxConnection.Invoker.Invoke(
                    "TdxHq_GetHistoryMinuteTimeData",
                    typeof(QuoteDelegate.TdxHq_GetHistoryMinuteTimeData));

            var zqdm = stkList.Select(p => p.StkCode).ToArray();
            var market = stkList.Select(p => Convert.ToInt16(p.Type) == 0 ? (byte) 0 : (byte) 1).ToArray();

            int date = 20190108;
            var dateTime = DateTime.Now;

            Log.Debug("====== delay = " + (DateTime.Now - dateTime).TotalMilliseconds + "=================");
            dateTime = DateTime.Now;
            for (var i = 0; i < stkList.Count; i++)
            {
                var stockCode = zqdm[i];
                var marketCode = market[i];
                var success = tdxHqGetHistoryTransactionData(marketCode, stockCode, date, sResult, sErrInfo);
                if (!success)
                {
                    continue;
                }

                sResult.Clear();
                sErrInfo.Clear();
            }
            Log.Debug("====== delay = " + (DateTime.Now - dateTime).TotalMilliseconds + "=================");

            return null;
        }


        /// <summary>
        /// 获取当日分数据
        /// </summary>
        /// <param name="tdxConnection"></param>
        /// <param name="stklist"></param>
        /// <returns></returns>
        public static Dictionary<string,List<StkMin>> GetMinuteTimeData(TdxConnection tdxConnection, List<Stkinfo> stkList)
        {
            var date = int.Parse(DateTime.Now.ToString("yyyyMMdd"));
            StringBuilder sErrInfo = new StringBuilder(256);
            StringBuilder sResult = new StringBuilder(1024 * 1024);
            
            QuoteDelegate.TdxHq_GetHistoryMinuteTimeData getHistoryMinuteTimeData =
                (QuoteDelegate.TdxHq_GetHistoryMinuteTimeData) tdxConnection.Invoker.Invoke(
                    "TdxHq_GetHistoryMinuteTimeData",
                    typeof(QuoteDelegate.TdxHq_GetHistoryMinuteTimeData));

            var zqdm = stkList.Select(p => p.StkCode).ToArray();
            var market = stkList.Select(p => Convert.ToInt16(p.Type) == 0 ? (byte) 0 : (byte) 1).ToArray();

            var dictionary = new Dictionary<string,List<StkMin>>(stkList.Count);
            for (var i = 0; i < stkList.Count; i++)
            {
                var stkMins = new List<StkMin>();
                var stockCode = zqdm[i];
                var marketCode = market[i];
                dictionary.Add(stockCode,stkMins);
               
                var success = getHistoryMinuteTimeData(marketCode, stockCode, date, sResult, sErrInfo);
                
                if (!success)
                {
                    return null;
                }
                var strings = sResult.ToString().Split('\n');
                var stringsLength = strings.Length;
                for (var j = 0; j < stringsLength; j++)
                {
                    if (j == 0) continue;
                    var row = strings[j].Split('\t');
                    var stkMin = new StkMin
                    {
                        Vol = int.Parse(row[1]),
                        Price = Single.Parse(row[0])
                    };
                    stkMins.Add(stkMin);
                }
                //清空数据
                sResult.Clear();
                sErrInfo.Clear();
            }

            return dictionary;
        }

        /// <summary>
        /// 获取除权除息信息
        /// </summary>
        /// <param name="tdxConnection"></param>
        /// <param name="stklist"></param>
        /// <returns></returns>
        public static Dictionary<string, List<ExDividend>> TdxGetExDividendInfo(TdxConnection tdxConnection,
            List<Stkinfo> stklist)
        {
            StringBuilder sErrInfo = new StringBuilder(256);
            StringBuilder sResult = new StringBuilder(1024 * 1024);

            QuoteDelegate.TdxHq_GetXDXRInfo getXdxrInfo =
                (QuoteDelegate.TdxHq_GetXDXRInfo) tdxConnection.Invoker.Invoke("TdxHq_GetXDXRInfo",
                    typeof(QuoteDelegate.TdxHq_GetXDXRInfo));
            var zqdm = stklist.Select(p => p.StkCode).ToArray();
            var market = stklist.Select(p => Convert.ToInt16(p.Type) == 0 ? (byte) 0 : (byte) 1).ToArray();

            var dictionary = new Dictionary<string, List<ExDividend>>();
            for (var i = 0; i < stklist.Count; i++)
            {
                var exDividends = new List<ExDividend>();
                var success = getXdxrInfo(market[i], zqdm[i], sResult, sErrInfo);

                if (!success)
                {
                    continue;
                }

                var rows = sResult.ToString().Split('\n');
                for (var j = 1; j < rows.Length; j++)
                {
                    var cells = rows[j].Split('\t');
                    if (Convert.ToInt32(cells[3]) != 1)
                    {
                        continue;
                    }
                    var cashDividend = Convert.ToDouble(cells[4]);
                    var stockDividend = Convert.ToDouble(cells[6]);
                    if (Math.Abs(cashDividend) <= 0 && Math.Abs(stockDividend) <= 0)
                    {
                        continue;
                    }
                    exDividends.Add(new ExDividend
                    {
                        StockCode = stklist[i].StkCode,
                        Date = cells[2].Trim(),
                        CashDividend = cashDividend,
                        StockDividend = stockDividend,
                        CashDividendRate = cashDividend / 10d,
                        StockDividendRate = stockDividend / 10d,
                    });
                }

                dictionary.Add(stklist[i].StkCode, exDividends);

                sResult.Clear();
                sErrInfo.Clear();
            }

            return dictionary;
        }


        /// <summary>
        /// 获取所有A股代码  耗时1s
        /// </summary>
        /// <returns></returns>
        public static List<Stkinfo> TdxGetStkcode(TdxConnection tdxConnection)
        {
            StringBuilder sErrInfo = new StringBuilder(256);
            StringBuilder sResult = new StringBuilder(1024 * 1024);
            //byte markettype = Stkcode.Substring(7, 2) == "SH" ? (byte)1 : (byte)0;

            short Result = new short();
            List<Stkinfo> Trans = new List<Stkinfo>();
            bool bRet;
            short count = 0;

            ///获取深A代码
            QuoteDelegate.TdxHq_GetSecurityCount getSecurityCountDelegate =
                (QuoteDelegate.TdxHq_GetSecurityCount) tdxConnection.Invoker.Invoke("TdxHq_GetSecurityCount",
                    typeof(QuoteDelegate.TdxHq_GetSecurityCount));
            bRet = getSecurityCountDelegate(0, ref Result, sErrInfo);
            count = 1000;
            for (int i = 0; i <= Result / 1000; i++)
            {
                QuoteDelegate.TdxHq_GetSecurityList getSecurityList =
                    (QuoteDelegate.TdxHq_GetSecurityList) tdxConnection.Invoker.Invoke("TdxHq_GetSecurityList",
                        typeof(QuoteDelegate.TdxHq_GetSecurityList));
                getSecurityList(0, (short) (i * 1000), ref count, sResult, sErrInfo);

                var a = sResult.ToString().Split('\n').ToList();
                a.RemoveAt(0);
                foreach (var item in a)
                {
                    var b = item.Split('\t');
                    if (b[0].StartsWith("300") || b[0].StartsWith("00"))
                        Trans.Add(new Stkinfo
                        {
                            StkCode = b[0],
                            StkBaseVol = Convert.ToInt16(b[1]),
                            Stkname = b[2],
                            Save1 = b[3],
                            Save2 = b[4],
                            PreClose = Convert.ToDecimal(b[5]),
                            Save3 = b[6],
                            Type = "0"
                        });
                }
            }

            ///获取沪A代码
            QuoteDelegate.TdxHq_GetSecurityCount getSecurityCount =
                (QuoteDelegate.TdxHq_GetSecurityCount) tdxConnection.Invoker.Invoke("TdxHq_GetSecurityCount",
                    typeof(QuoteDelegate.TdxHq_GetSecurityCount));
            bRet = getSecurityCount(1, ref Result, sErrInfo);
            for (int i = 0; i <= Result / 1000; i++)
            {
                QuoteDelegate.TdxHq_GetSecurityList getSecurityList =
                    (QuoteDelegate.TdxHq_GetSecurityList) tdxConnection.Invoker.Invoke("TdxHq_GetSecurityList",
                        typeof(QuoteDelegate.TdxHq_GetSecurityList));
                getSecurityList(1, (short) (i * 1000), ref count, sResult, sErrInfo);
                var a = sResult.ToString().Split('\n').ToList();
                a.RemoveAt(0);
                foreach (var item in a)
                {
                    var b = item.Split('\t');
                    if (b[0].StartsWith("60"))
                        Trans.Add(new Stkinfo
                        {
                            StkCode = b[0],
                            StkBaseVol = Convert.ToInt16(b[1]),
                            PreClose = Convert.ToDecimal(b[5]),
                            Stkname = b[2],
                            Type = "1"
                            //Save1 = b[3],
                            //Save2 = b[4],                         
                            //Save3 = b[6]
                        });
                }
            }
            Log.Error(sErrInfo.ToString() == "" ? "获取全A市场代码--成功" : "获取全A市场代码--失败");

//            foreach (var stkinfos in Trans.Partition((Trans.Count + 1) / 10))
//            {
//                Quotes(tdxConnection, stkinfos);
//            }
            return Trans;
        }


        ///  <summary>
        /// 返回批量行情  最大值50
        ///  </summary>
        /// <param name="tdxConnection"></param>
        /// <param name="stklist"></param>
        ///   0市场	1代码	2活跃度	3现价	4昨收	5开盘	6最高	7最低	8时间	9保留	10总量	11现量	12总金额	13内盘	14外盘	15保留	16保留	17买一价	18卖一价	19买一量	20卖一量	21买二价	22卖二价	23买二量	24卖二量	25买三价	26卖三价	27买三量	28卖三量	29买四价	30卖四价	31买四量	32卖四量	33买五价	34卖五价	35买五量	36卖五量	37保留	38保留	39保留	40保留	41保留	42涨速	43活跃度
        ///  <returns></returns>
        public static IEnumerator<List<QuoteRtN>> Quotes(TdxConnection tdxConnection, List<Stkinfo> stklist)
        {
            StringBuilder sErrInfo = new StringBuilder(256);
            StringBuilder sResult = new StringBuilder(1024 * 1024);
         
            var totalCount = 0;
            DateTime now = DateTime.Now;
            var zqdm = stklist.Select(p => p.StkCode).ToList();
            var market = stklist.Select(p => Convert.ToInt16(p.Type) == 0 ? (byte) 0 : (byte) 1).ToList();
            //游标
            int index = 0;
            const int reqCount = 100;
            while (totalCount != stklist.Count)
            {
                DateTime startTime = DateTime.Now;
                List<QuoteRtN> quote = new List<QuoteRtN>();
                short count = (short) (stklist.Count - totalCount > reqCount ? reqCount : stklist.Count - totalCount);
                byte[] zqdmArray = market.GetRange(totalCount, count).ToArray();
                string[] marketArray = zqdm.GetRange(totalCount, count).ToArray();
                QuoteDelegate.TdxHq_GetSecurityQuotes getSecurityQuotes =
                    (QuoteDelegate.TdxHq_GetSecurityQuotes) tdxConnection.Invoker.Invoke("TdxHq_GetSecurityQuotes",
                        typeof(QuoteDelegate.TdxHq_GetSecurityQuotes));

                bool success;
                try
                {
                    success = getSecurityQuotes(zqdmArray, marketArray, ref count, sResult, sErrInfo);
                }
                catch (Exception e)
                {
                    EmailSenter.SendEmail("TdxApiPool getSecurityQuotes error", e.Message);
                    throw e;
                }

                if (sErrInfo.ToString() != "" || !success)
                {

                    Log.Error("L2Quotes--失败");
                    yield break;
                }

                //获取5档行情
                //bool bool12 = TradeX.TdxL2Hq_GetSecurityQuotes(Market, Zqdm, ref Count, sResult, sErrInfo);
                var a = sResult.ToString().Split('\n').ToList();
                a.RemoveAt(0);
                var time = DateTime.Now.ToString("MMddHHmmss");
                foreach (var item in a)
                {
                    var b = item.Split('\t');
                    var charArray = stklist[index].Stkname.ToCharArray();
                    quote.Add(Tdx2QuoteStruct(charArray, b, time));
                    index++;
                }
                sResult.Clear();
                sErrInfo.Clear();
                totalCount = totalCount + count;
                var endTime = DateTime.Now;
//                Log.Debug("ip=" + tdxConnection.Ip + "\ttotalDelay=" + (endTime - startTime).TotalMilliseconds +
//                              "\tstockDelay=" + (endTime - startTime).TotalMilliseconds / totalCount +
//                              "\ttotalCount=" + totalCount + "\tlistSize=" + stklist.Count);
                yield return quote;
            }

            var dateTime = DateTime.Now;
            Log.Debug("ip=" + tdxConnection.Ip + "\ttotalDelay=" + (dateTime - now).TotalMilliseconds +
                              "\tstockDelay=" + (dateTime - now).TotalMilliseconds / totalCount +
                              "\ttotalCount=" + totalCount + "\tlistSize=" + stklist.Count);
        }

        private static QuoteRtN Tdx2QuoteStruct(char[] stkName, string[] data, string time)
        {
            return new QuoteRtN
            {
                Market = UInt16.Parse(data[0]),
                StkCode = UInt32.Parse(data[1]),
                StkName = stkName,
                Vitality = Convert.ToSingle(data[2]),
                LastPrice = Convert.ToSingle(data[3]),
                Time = Convert.ToUInt32(time),
                PreClose = Convert.ToSingle(data[4]),
                Open = Convert.ToSingle(data[5]),
                High = Convert.ToSingle(data[6]),
                Low = Convert.ToSingle(data[7]),
                Bid1 = Convert.ToSingle(data[17]),
                VolAll = Convert.ToSingle(data[10]),
                Ask1 = Convert.ToSingle(data[18]),
                Bsize1 = Convert.ToInt32(data[19]),
                Asize1 = Convert.ToInt32(data[20]),
                Velocity5Min = Convert.ToSingle(data[42])
            };
        }

        public class GetQuoteErrorException : ApplicationException
        {
            private readonly string _error;

            private Exception _innerException;

            //无参数构造函数
            public GetQuoteErrorException()
            {
            }

            //带一个字符串参数的构造函数，作用：当程序员用Exception类获取异常信息而非 MyException时把自定义异常信息传递过去
            public GetQuoteErrorException(string msg) : base(msg)
            {
                this._error = msg;
            }

            //带有一个字符串参数和一个内部异常信息参数的构造函数
            public GetQuoteErrorException(string msg, Exception innerException) : base(msg)
            {
                this._innerException = innerException;
                this._error = msg;
            }

            public string GetError()
            {
                return _error;
            }
        }
    }
}